
from rgblink_cmd import *
from process_cmd import *
import socket
import queue
import threading
import time
import serial
import serial.tools.list_ports
import sys
import os

def getDataBlockCheckSum(dataBlock):
    checkSum = 0
    for i in range(0, len(dataBlock)):
        checkSum = (checkSum+dataBlock[i])
    checkSum = checkSum % 256
    return checkSum

class connectDeviceSet:
	def __init__(self, connectType=0, localAddr=('127.0.0.1', 0), remoteAddr=('192.168.0.100', 1000)):
		self.connectType = connectType
		self.localAddr = localAddr
		self.remoteAddr = remoteAddr
		self.type = 0
		self.sn = 0
		self.com = "COM1"
	def __str__(self):
		if(self.connectType == 0):
			return self.getTypeStr()+" IP:"+str(self.remoteAddr[0])+" SN:{:04X}".format(self.sn)
		elif(self.connectType == 1):
			return self.getTypeStr()+" COM:"+str(self.com)+" SN:{:04X}".format(self.sn)
	def getTypeStr(self):
		if(self.type == 0x800000):
			return "X14"
		elif(self.type == 0x320000):
			return "X3"
		elif(self.type == 0x700000):
			return "X7"
		elif(self.type == 0x321000):
			return "M3"
		elif(self.type == 0x240000):
			return "F16"
		elif(self.type == 0x220000):
			return "X2"
		elif(self.type == 0x628200):
			return "D6"
		elif(self.type == 0x629000):
			return "D4"
		else:
			return "{:06X}".format(self.type)
	

class device:
	def __init__(self, connectType=0, localAddr=('127.0.0.1', 0), remoteAddr=('192.168.0.100', 1000)):
		self.connectType=connectType
		self.localAddr = localAddr
		self.remoteAddr = remoteAddr
		self.comFd = None
		self.com_receive_thread = None
		self.com_thread_stop = False
		self.socket = None
		self.udp_receive_thread = None
		self.udp_thread_stop = False
		self.process_cmd = process_cmd()
		self.set = self.process_cmd.device_set
		self.set.sendCmd = self.SendCmd
		self.set.waitACK_func = self.waitACK
		self.deviceList = []
		self.receive_callback = None
		self.udp_send_message = bytes()
		self.udp_send_time = time.time()
	def printAllDevice(self):
		for i in range(0, len(self.deviceList)):
			print(i, ":", self.deviceList[i])
	def connect_device(self, index):
		self.disconnect()
		if(index < len(self.deviceList)):
			if(self.deviceList[index].connectType == 0):
				self.connect_ip_device(self.deviceList[index])
			elif(self.deviceList[index].connectType == 1):
				self.connect_com_device(self.deviceList[index])
	def disconnect(self):
		if(self.connectType == 0):
			self.disconnect_ip()
		if(self.connectType == 1):
			self.disconnect_com();
		
	def connect_com_device(self, deviceSet1):
		self.connect_com(deviceSet1.com)
	def disconnect_com(self):
		if(self.comFd):
			self.com_thread_stop = True
			self.com_receive_thread.join(2)
			self.com_receive_thread = None
			self.comFd.close()
			self.comFd = None
	def connect_ip_device(self, deviceSet1):
		self.connect_ip(deviceSet1.localAddr, deviceSet1.remoteAddr)
	def connect_ip(self, localAddr=(b'127.0.0.1', 0), remoteAddr=(b'192.168.0.100', 1000)):
		if(self.socket):
			self.disconnect_ip()
			
		self.localAddr = localAddr
		self.remoteAddr = remoteAddr
		self.connectType=0
		self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF,32*1024)
		print("socket rcv buffer size =", self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF))
		self.socket.bind(self.localAddr)
		self.socket.settimeout(0.5)
		self.udp_receive_thread = threading.Thread(target=self.receive_udp, name='worker')
		self.udp_thread_stop = False
		self.udp_receive_thread.start()
	def connect_com(self, com):
		if(self.comFd):
			self.disconnect_com()
		print("connect", com)
		self.connectType = 1
		self.comFd = serial.Serial(com, 115200, timeout = 0.5)
		self.com_receive_thread = threading.Thread(target=self.receive_com, name='worker')
		self.com_thread_stop = False
		self.com_receive_thread.start()
		
	def disconnect_ip(self):
		if(self.socket):
			self.udp_thread_stop = True
			self.udp_receive_thread.join(2)
			self.udp_receive_thread = None
			self.socket.close()
			self.socket = None
			
	
	def receive_udp(self):
		while 1:
			if(self.udp_thread_stop):
				return
			if(self.socket == None):
				return
			try:
				data, server_addr = self.socket.recvfrom(2048)
				if(self.receive_callback):
					self.receive_callback(data)
					None
				if(len(data)):
					#print("receive len =", len(data))
					self.process_cmd.interpret_cmd(data)
					
					#print(data, server_addr)
			except socket.timeout as err:
				if(self.receive_callback):
					self.receive_callback(bytes())
					None
				None
	def receive_com(self):
		while 1:
			if(self.com_thread_stop):
				return
			if(self.comFd == None):
				return
				
			try:
				data = self.comFd.read(19)
				if(self.receive_callback):
					self.receive_callback(data)
				if(len(data)):
					#print(data)
					#print("receive len =", len(data))
					self.process_cmd.interpret_cmd(data)
					
			except:
				None
	
	def com_send(self, message):
		if(self.comFd) and (len(message)>0):
			self.comFd.write(message)
		
	def udp_send(self, message):
		if(self.socket):
			
			current_time = time.time()
			if((current_time-self.udp_send_time)<0.01) and (len(message) != 0) and (len(self.udp_send_message)<1024):
				self.udp_send_message += message
				if (len(self.udp_send_message)<1152):
					return
				
			message = self.udp_send_message+message
			self.udp_send_message = bytes()
				
			sendLen = self.socket.sendto(message, self.remoteAddr)
			time.sleep(0.005) # 要延时3ms，否则设备接收会丢失数据包
			#print("send len =", sendLen)
			self.udp_send_time = time.time()
			while(sendLen < len(message)):
				#print("send sleep")
				sendLen = self.socket.sendto(message, self.remoteAddr)
				time.sleep(0.003)
	def send(self, message):
		if(self.connectType == 0):
			self.udp_send(message)
		if(self.connectType == 1):
			self.com_send(message)
	def SendCmd(self, adr = 0, cmd = 0, dat1 = 0, dat2 = 0, dat3 = 0, dat4 = 0, dataBlock=bytes()):
		self.process_cmd.sendSn = self.process_cmd.sendSn+1
		self.process_cmd.sendSn = self.process_cmd.sendSn%256
		dataLen = len(dataBlock)
		if((dataLen>0) and (cmd != 0x9A)):
			dat3 = (dataLen+1) % 256
			dat4 = ((dataLen+1) // 256) % 256
		tempcmd = rgblink_cmd(adr=adr, sn=self.process_cmd.sendSn, cmd=cmd, data1=dat1, data2=dat2, data3=dat3, data4=dat4)
		sendBytes = tempcmd.toSendBytes()
		if(dataLen>0):
			sendBytes = sendBytes+dataBlock+getDataBlockCheckSum(dataBlock).to_bytes(1, byteorder='little')
		
		self.send(sendBytes)
		return self.process_cmd.sendSn
	def waitACK(self, timeout = 1):
		self.send(bytes())
		startTime = time.time()
		endTime = startTime+timeout
		while(time.time()<endTime):
			time.sleep(0.01)
			#print("waitACK sleep 0.1")
			if(self.process_cmd.sendSn == self.process_cmd.receiveSn):
				#print("sn:",  self.process_cmd.receiveSn) 
				return self.process_cmd.cmd
		#print("sendSn=", self.process_cmd.sendSn, "receiveSn=", self.process_cmd.receiveSn)
		return None
	def getHostAddrs():
		addrList = []
		s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		#print(socket.gethostbyname(socket.gethostname()))
		addrs = socket.getaddrinfo(socket.gethostname(), None)
		s.close()
		for item in addrs:
			if ':' not in item[4][0]:
				addrList += [item[4]]
				#print(item[4])
		return addrList
		


	def findUdpDevice(self, host_addr):
		s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		s.bind(host_addr)
		message = b"<T0000680100000069>"
		s.settimeout(0.6)
		remote_addr = host_addr[0]
		temp = remote_addr.split('.');
		remote_addr = temp[0]+"."+temp[1]+"."+temp[2]+".255"
		print(remote_addr)
		s.sendto(message, (remote_addr, 1000))
		while(1):
			try:
				data, server_addr = s.recvfrom(1024)  
				cmd = self.process_cmd.process_cmd(data)
				if(cmd):
					if((cmd.cmd == 0x68) and (cmd.data[0] == 0x01)):
						device_type = (cmd.data[1]<<16)+(cmd.data[2]<<8)+cmd.data[3]
						deviceSet = connectDeviceSet(0, host_addr, server_addr)
						deviceSet.type = device_type
						self.deviceList = self.deviceList+[deviceSet]
						#print(data, server_addr)
			except socket.timeout as err:
				#print(err)
				break
		s.close()
	def readAllUdpDeviceSn(self):
		for i in self.deviceList:
			if(i.connectType == 0):
				self.readUdpDeviceSn(i)
	def readUdpDeviceSn(self, deviceSet):
		s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		s.bind(deviceSet.localAddr)
		message = b'<T00006A110000007B>'
		s.settimeout(0.5)
		s.sendto(message, deviceSet.remoteAddr)
		#print("readUdpDeviceSn", deviceSet.remoteAddr)
		try:
			data, server_addr = s.recvfrom(1024)  
			
			cmd = self.process_cmd.process_cmd(data)
			#print(cmd)
			if(cmd):
				if((cmd.cmd==0x6A) and (cmd.data[0] == 0x11)):
					sn = (cmd.data[1]<<8)+cmd.data[2]
					deviceSet.sn = sn
					#print(data, server_addr)
					#print(sn)
		except socket.timeout as err:
			print("readUdpDeviceSn:", err)
			None 
		except WindowsError as err:
			print(err)
		else:
			None
		s.close()
	def findAllUdpDevice(self):
		host_addr_list = device.getHostAddrs()
		for host_addr in host_addr_list:
			self.findUdpDevice(host_addr)
		#self.readAllUdpDeviceSn()
	def getComList():
		plist = list(serial.tools.list_ports.comports())
		comNameList = []
		for plist_0 in plist:
			comNameList.append(plist_0[0])
		#print(comNameList)
		return comNameList
	def findAllComDevice(self):
		com_list = device.getComList()
		for com in com_list:
			self.findComDevice(com)
		#self.readAllComDeviceSn()
	def findComDevice(self, com):
		comFd = None
		try:
			comFd = serial.Serial(com, 115200, timeout = 0.5)
		except serial.serialutil.SerialException as err:
			print(err)
			return
		message = b"<T0000680100000069>"
		comFd.write(message)
		try:
			data = comFd.read(1024)
			cmd = self.process_cmd.process_cmd(data)
			if(cmd):
				if((cmd.cmd == 0x68) and (cmd.data[0] == 0x01)):
					device_type = (cmd.data[1]<<16)+(cmd.data[2]<<8)+cmd.data[3]
					deviceSet = connectDeviceSet()
					deviceSet.connectType = 1
					deviceSet.com = com
					deviceSet.type = device_type
					self.deviceList = self.deviceList+[deviceSet]
		except WindowsError as err:
			print(err)
			return
			
	def readComDeviceSn(self, deviceSet):
		comFd = None
		try:
			comFd = serial.Serial(deviceSet.com, 115200, timeout = 0.5)
		except serial.serialutil.SerialException as err:
			print(err)
			return
		message = b"<T00006A110000007B>"
		comFd.write(message)
		try:
			data = comFd.read(1024)
			cmd = self.process_cmd.process_cmd(data)
			if(cmd):
				if(cmd.cmd == 0x6A and (cmd.data[0] == 0x11)):
					sn = (cmd.data[1]<<8)+cmd.data[2]
					deviceSet.sn = sn
		except err:
			print(err)
			return
	def readAllComDeviceSn(self):
		for i in self.deviceList:
			if(i.connectType == 1):
				self.readComDeviceSn(i)
	def findAllDevice(self):
		self.findAllUdpDevice()
		self.findAllComDevice()
		self.readAllComDeviceSn()
		self.readAllUdpDeviceSn()
if __name__ == "__main__":

	device1 = device()
	device1.findAllDevice()
	device1.printAllDevice()
	device1.connect_com("COM5")
	#device1.connect_ip(('192.168.0.111', 0), ('192.168.0.100', 1000))
	device1.set.sync()
	device1.waitACK(10)
	device1.set.printAllLayerAlpha()
	
	